cmake_minimum_required(VERSION 3.10)

# Project name
project(AllocHookProject)

# Generate compile_commands.json to enable clang-tidy and cppcheck
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Add git revision
find_package(Git REQUIRED)
if(GIT_FOUND)
    execute_process(
        COMMAND ${GIT_EXECUTABLE} describe --always --exclude "*"
        WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
        OUTPUT_VARIABLE GIT_REVISION
        OUTPUT_STRIP_TRAILING_WHITESPACE
    )
    add_compile_definitions(ALLOC_HOOK_VERSION=0x${GIT_REVISION})
endif()

# Set compile options globally
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -I. -g -O1")

# Include directories
include_directories(${CMAKE_SOURCE_DIR})

# heap_parse target
add_executable(heap_parse heap_parse.cc)
target_link_libraries(heap_parse z)

# x86 target
add_library(liballoc_hook_x86 SHARED alloc_hook.cc)
target_compile_options(liballoc_hook_x86 PRIVATE -fPIC -I. -g -O0)
target_link_libraries(liballoc_hook_x86 PRIVATE pthread z)

# Custom targets for specific toolchains
function(add_custom_toolchain_target target_name output_name source_file)
    add_custom_target(${target_name}
        COMMAND aarch64-linux-g++ -fPIC -shared -I. -g -O1 ${source_file} -o ${output_name}
        DEPENDS heap_parse
        WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
    )
endfunction()

add_custom_toolchain_target(liballoc_hook_aarch64 liballoc_hook_aarch64.so alloc_hook.cc)

# example target
add_executable(example example.cc)
target_link_libraries(example z)

# aarch64_example target
add_custom_target(aarch64_example
    COMMAND aarch64-linux-g++ -lz -o aarch64_example example.cc
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)

option(ENABLE_STATIC_CHECK "Enable static check" OFF)
if(ENABLE_STATIC_CHECK)
# Find all .cc files in the project source directory
file(GLOB_RECURSE ALL_CPP_FILES ${CMAKE_SOURCE_DIR}/*.cc)

# Run cppcheck and generate a flag file if there are any warnings or errors
add_custom_target(run_cppcheck
    COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/cppcheck_failed
    COMMAND cppcheck --project=${CMAKE_BINARY_DIR}/compile_commands.json
    --enable=style
    --enable=performance
    --enable=portability
    --check-level=exhaustive
    --enable=unusedFunction
    --suppress=information
    --suppress=missingIncludeSystem
    --suppress=unmatchedSuppression
    --suppress=passedByValue
    --suppress=unusedFunction
    --suppress=uninitMemberVar
    --suppress=uninitvar
    --suppress=nullPointer
    --suppress=shadowVariable
    --suppress=variableScope
    --suppress=duplicateExpression
    --suppress=useInitializationList
    -I /usr/include
    -I /usr/local/include
    --error-exitcode=1
    || ${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/cppcheck_failed
    COMMENT "Running cppcheck"
    VERBATIM)

add_custom_target(check_cppcheck
    COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target run_cppcheck
    COMMENT "Checking cppcheck results"
    VERBATIM)

# Ensure run_cppcheck and check_cppcheck are run before building any target
add_dependencies(heap_parse check_cppcheck)
add_dependencies(liballoc_hook_x86 check_cppcheck)
add_dependencies(example check_cppcheck)

# Function to add Cppcheck pre-build check
function(add_cppcheck_pre_build target_name)
    add_custom_command(TARGET ${target_name} PRE_BUILD
        COMMAND ${CMAKE_COMMAND} -E echo "Running Cppcheck pre-build check for ${target_name}"
        COMMAND /bin/bash -c
        "if [ -f ${CMAKE_BINARY_DIR}/cppcheck_failed ]; \
        then echo 'Stopping build due to Cppcheck errors.'; \
        exit 1; else echo 'No Cppcheck issues found. Continuing build.'; fi"
        COMMENT "Checking for Cppcheck issues before building ${target_name}"
        VERBATIM)
endfunction()

# Add Cppcheck pre-build check for targets
add_cppcheck_pre_build(heap_parse)
add_cppcheck_pre_build(liballoc_hook_x86)
add_cppcheck_pre_build(example)

# Clang-tidy custom targets for running and checking results
add_custom_target(run_clang_tidy
    COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/clang_tidy_failed
    COMMAND clang-tidy -p ${CMAKE_BINARY_DIR} --warnings-as-errors=* ${ALL_CPP_FILES} || ${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/clang_tidy_failed
    COMMENT "Running clang-tidy"
    VERBATIM)

add_custom_target(check_clang_tidy
    COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target run_clang_tidy
    COMMENT "Checking clang-tidy results"
    VERBATIM)

# Ensure run_clang_tidy and check_clang_tidy are run before building any target
add_dependencies(heap_parse check_clang_tidy)
add_dependencies(liballoc_hook_x86 check_clang_tidy)
add_dependencies(example check_clang_tidy)

# Function to add Clang-Tidy pre-build check
function(add_clang_tidy_pre_build target_name)
    add_custom_command(TARGET ${target_name} PRE_BUILD
        COMMAND ${CMAKE_COMMAND} -E echo "Running Clang-Tidy pre-build check for ${target_name}"
        COMMAND /bin/bash -c "if [ -f ${CMAKE_BINARY_DIR}/clang_tidy_failed ]; then echo 'Stopping build due to Clang-tidy errors.'; exit 1; else echo 'No Clang-tidy issues found. Continuing build.'; fi"
        COMMENT "Checking for Clang-tidy issues before building ${target_name}"
        VERBATIM)
endfunction()

# Add Clang-Tidy pre-build check for targets
add_clang_tidy_pre_build(heap_parse)
add_clang_tidy_pre_build(liballoc_hook_x86)
add_clang_tidy_pre_build(example)
endif()
